////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Filename:    ParadiseDreamsGuts.cpp                                       //
//  Developer:   Tommy Riddle                                                 //
//  Create date: 05/18/2006                                                   //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//                                                                            //
//  Description: This is the class implementation for the slot gut of the     //
//               Australian Slot machine called Paradise Dreams.              //
//                                                                            //
////////////////////////////////////////////////////////////////////////////////
//        Copyright (c) 2006 - Realtime Gaming.  All rights reserved.         //
////////////////////////////////////////////////////////////////////////////////

#include "ParadiseDreamsGuts.h"
#include "slotconn.h"
#include "getrand.h"
#include "sltsql.h"

////////////////////////////////////////////////////////////////////////////
/// ParadiseDreamsGuts::m_aLineWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by wheel icon and then count.  So to get the
/// SpinResult for 5 Aces, use the following code snippet:
///
///		SpinResult result = m_aLineWinSpinResults[ ICON_ACE ][ 5 ];
///
/// In this example result would be set to EVT_FIVE_ACE.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::m_aLineWinSpinResults[][6] = 
{
// Number of icons:			0			1				2					3					4					5
/* ICON_NINE */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_NINE,		EVT_FOUR_NINE,		EVT_FIVE_NINE		},
/* ICON_TEN */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_TEN,		EVT_FOUR_TEN,		EVT_FIVE_TEN		},
/* ICON_JACK */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_JACK,		EVT_FOUR_JACK,		EVT_FIVE_JACK		},
/* ICON_QUEEN */		{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_QUEEN,	EVT_FOUR_QUEEN,		EVT_FIVE_QUEEN		},
/* ICON_KING */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_KING,		EVT_FOUR_KING,		EVT_FIVE_KING		},
/* ICON_ACE */			{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_THREE_ACE,		EVT_FOUR_ACE,		EVT_FIVE_ACE		},
/* ICON_COCONUTS */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_COCONUTS,	EVT_THREE_COCONUTS,	EVT_FOUR_COCONUTS,	EVT_FIVE_COCONUTS	},
/* ICON_FLOWER */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_FLOWER,		EVT_THREE_FLOWER,	EVT_FOUR_FLOWER,	EVT_FIVE_FLOWER		},
/* ICON_FISH */			{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_FISH,		EVT_THREE_FISH,		EVT_FOUR_FISH,		EVT_FIVE_FISH		},
/* ICON_PARROT */		{	EVT_LOSER,	EVT_LOSER,		EVT_TWO_PARROT,		EVT_THREE_PARROT,	EVT_FOUR_PARROT,	EVT_FIVE_PARROT		},
/* ICON_BUTTERFLY */	{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_ISLAND */		{	EVT_LOSER,	EVT_LOSER,		EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			},
/* ICON_GIRL */			{	EVT_LOSER,	EVT_ONE_GIRL,	EVT_LOSER,			EVT_LOSER,			EVT_LOSER,			EVT_LOSER			}
};

////////////////////////////////////////////////////////////////////////////
/// ParadiseDreamsGuts::m_aScatterWinSpinResults
///
/// This array is used to quickly get the SpinResult for the number of
/// icons.  It is indexed by icon count only, since Islands are the only
/// scatter win.  So to get the SpinResult for 5 Islands, use the
/// following code snippet:
///
///		SpinResult result = m_aScatterWinSpinResults[ 5 ];
///
/// In this example result would be set to EVT_FIVE_ISLAND.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::m_aScatterWinSpinResults[6] = 
{
	// ICON_ISLAND		   Number of Icons
	EVT_LOSER,			// 0
	EVT_LOSER,			// 1
	EVT_TWO_ISLAND,		// 2
	EVT_THREE_ISLAND,	// 3
	EVT_FOUR_ISLAND,	// 4
	EVT_FIVE_ISLAND		// 5
};

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::ParadiseDreamsGuts()   
///
/// This is the default constructor for the class.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::ParadiseDreamsGuts()
{
	// If this doesn't compile it means that someone probably added
	// another icon type but did not update the
	// m_aLineWinSpinResults array. 
	C_ASSERT( countof( m_aLineWinSpinResults ) == NUM_VALID_ICONS );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::~ParadiseDreamsGuts()
///
/// Destructor for ParadiseDreamsGuts. 
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::~ParadiseDreamsGuts()
{
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::getGirlLineWinResult( uint8* stops ) const
///
/// @param   stops			The array of stop positions for the line.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the Girl line win payout.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::getGirlLineWinResult( uint8* stops ) const
{
	// Count how many Girls we have at the beginning of the line.
	for ( uint8 i = 0; i < getReelCount(); i++ )
    {
		// If this icon is not a Girl...
		if ( getIcon( getGameModeId(), i, stops[i] )->iconid != ICON_GIRL )
		{
			// then we have our total.
			break;
		}
    }

	// Use the array to look up the spin result for i Girls.
	return lookupLineWinSpinResult( ICON_GIRL, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::getLineWinResultWithPossibleSubstitution( uint8* stops, uint8 testIcon, bool& bSubstitution ) const
///
/// @param   stops			The array of stop positions for the line.
/// @param   testIcon		The icon we are looking for, typically the first icon on the line.
/// @param   bSubstitution	An output parameter that is set if a subsitution is used.
///
/// @return	 SpinResult		A payout from the SpinResult enumeration.
///
/// This helper method is used to calculate the line win payout with a possible substitution.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::getLineWinResultWithPossibleSubstitution(
	uint8* stops, uint8 testIcon, bool& bSubstitution ) const
{
	// Initialize the out parameter
	bSubstitution = false;

	// Loop through the remaining reels until the streak is over
	for ( int i = 1; i < getReelCount(); i++ )
    {
		// See what this icon is.
		uint8 currentIcon = getIcon( getGameModeId(), i, stops[i] )->iconid;

		// If the current icon is a Girl...
		if ( currentIcon == ICON_GIRL )
		{
			// then use it as a substitution symbol and continue on.
			bSubstitution = true;
			continue;
		}
		// Otherwise if the test icon is a Girl...
		else if ( testIcon == ICON_GIRL )
		{
			// then we still have a substitution.  In this case the line
			// started out with a Girl.
			bSubstitution = true;

			// We have now found our first icon that is not a Girl.  All
			// of the rest of the icons will have to match this one now for the
			// streak to continue, so change our test icon.
			testIcon = currentIcon;
			continue;
		}

		// If the current icon is not the test icon...
        if ( currentIcon != testIcon )
        {
			// then the streak is over so break out of the loop
			break;
        }
    }

	// If we made it all the way through and the testIcon is still a Girl...
	if ( testIcon == ICON_GIRL )
	{
		// then this must be a 5 Girl win, so reset the substitution flag.
		bSubstitution = false;

		// Actually this should never happen in this game because there aren't 
		// any Girls on some of the reels.
		ASSERT( false );
	}

	// Use the array to look up the spin result for i testIcons.
	return lookupLineWinSpinResult( testIcon, i );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 bool ParadiseDreamsGuts::doesGirlLineWinPayMore( SpinResult srGirl, SpinResult srOther, bool bSubstitution ) const
///
/// @param   srGirl         The spin result to calculate the Girl payout for.
/// @param   srOther		The spin result to calculate the other payout for.
/// @param   bSubstitution	Set if we should use the substitution multiplier with the other payout.
///
/// @return	 bool			True if the Girl win is larger.
///
/// This helper method is used to see which payout is larger.
////////////////////////////////////////////////////////////////////////////
bool ParadiseDreamsGuts::doesGirlLineWinPayMore( SpinResult srGirl,
	SpinResult srOther, bool bSubstitution ) const
{
	// Lookup the payout for the Girl line win.
	long nGirlLineWinPayout = getPayout( srGirl )->payoutAmount;

	// See what the total payout for the other line win is without the substitution
	// multiplier (if any).
	long nOtherLineWinPayout = getPayout( srOther )->payoutAmount;

	// If we had a substitution...
	if ( bSubstitution )
	{
		// then factor in the multiplier.
		nOtherLineWinPayout *= MULTIPLIER_SUBSTITUTION;
	}

	// Return true if the Girl payout is bigger.
    return ( nGirlLineWinPayout > nOtherLineWinPayout );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::classifyStops()   
///
/// @param   stops      The array of stop positions for the line.
/// @param   bet        The value of the line bet.
/// @param   line       The index of the line.
///
/// @return	 uint8      A payout from the SpinResult enumeration as a uint8.    
///
/// This helper method is used to calculate the payout on a line.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::classifyStops( SpinLine* spinline, uint8* stops, uint8 bet, uint8 line,
	uint8& rtnresult, uint8& multiplier )
{
	// Track the Girl result separately since it might be changed to a substitution.
	SpinResult srGirlLineWinResult = getGirlLineWinResult( stops );

	// Now see what the payout is if we use any Girls we find as substitution symbols.
	// Start with the icon on the first reel.
	uint8 testIcon = getIcon( getGameModeId(), 0, stops[0] )->iconid;

	// Declare a local variable to track whether or not we found a substitution.
	bool bSubstitution = false;

	// See what the spin result is if we used any Girls as substitution symbols.
	SpinResult srResultWithPossibleSubstitution =
		getLineWinResultWithPossibleSubstitution( stops, testIcon, bSubstitution );

	// Use the larger payout between the regular Girl evaluation compared to
	// the evaluation with the possible substitution.
    if ( doesGirlLineWinPayMore( srGirlLineWinResult,
		srResultWithPossibleSubstitution, bSubstitution ) )
	{
		// The Girls by themselves pay out more so use the Girl result.
		rtnresult = srGirlLineWinResult;
	}
	else
	{
		// Otherwise the result with the substitution (if any) paid more so use it.
		rtnresult = srResultWithPossibleSubstitution;

		// If we did actually have a substitution and a winning spin...
		if ( bSubstitution && srResultWithPossibleSubstitution != EVT_LOSER )
		{
			// then add a note and change the multiplier.
			spinline->addNote( NOTE_SUBSTITUTION );
			multiplier = MULTIPLIER_SUBSTITUTION;
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn		ParadiseDreamsGuts::tryForJackpot()
///
///	This function tries to win the jackpot and adds the scatter if we hit
/// the jackpot.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::tryForJackpot( SlotSpin* spin )
{
	// If we are in the first paid spin (NOT a bonus spin)...
	if ( getGameModeId() == MODE_NORMAL ) 
	{
		// then try for the Random Progressive Jackpot
		// If we hit the jackpot...
		if ( getRandomProgressiveJackpotResult( getNumLinesBet() ) || 
             m_bForceRandomJackpot == true )
		{
			// then add a jackpot scatter to the spin.
			spin->addScatter( 0,
				getPayout( EVT_RANDOM_PROGRESSIVE_WINNER )->payoutAmount, 1,
				true, EVT_RANDOM_PROGRESSIVE_WINNER, 0 );
		}
    }

	// CSN-5756 - Treasure Island
	// In third party games (API) we include the jackpot in every spin so that they can update it.
	// If no jackpot, they have not won the jackpot
	if (isThirdParty)
		setJackpotAmount( getProgressiveValue() );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getScatterWinResult()   
///
/// @param   spin						The current spin object.
/// @param   nNumScatterIcons			The number of scatter icons that we find.
/// @param   nScatterPositions			The bit mask for the scatters positions that we find.
///
/// @return	 SpinResult			A payout from the SpinResult enumeration.
///
/// This helper method is used to evaluate the spin for scatters.
////////////////////////////////////////////////////////////////////////////
ParadiseDreamsGuts::SpinResult ParadiseDreamsGuts::getScatterWinResult( SlotSpin* spin, int32& nNumScatterIcons, int32& nScatterPositions ) const
{
	// Initialize the scatterPositions.
	nScatterPositions = 0;

	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == ICON_ISLAND )
			{
				// then increase the icon count
				nNumScatterIcons++;

				// Set the bit in the position mask for this stop.
				nScatterPositions |= 1 << position;
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}

	// Use the array to look up the spin result for nNumScatterIcons.
	return lookupScatterWinSpinResult( nNumScatterIcons );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::evaluateScatterWin()
///
/// This function determines if the current spin wins a scatter payment and
/// adds the scatter.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::evaluateScatterWin( SlotSpin* spin, SpinResult srScatterResult, int32 nScatterPositions )
{
	// If we got the scatter win...
	if ( srScatterResult != EVT_LOSER )
	{
		// then initialize our multiplier for scatters.
		int32 multiplier = 1;

		// If we are in free spin mode...
		if ( getGameModeId() == MODE_FREE_SPIN )
		{
			// then use the free spin scatter multiplier.
			multiplier = MULTIPLIER_FREE_SPIN;
		}

		// Get the base payout for the scatter.
		int32 nPayout = getPayout( srScatterResult )->payoutAmount;
		
		// Scatters payout based on the total bet, so factor in the number of bet lines.
		nPayout *= getNumLinesBet();

		// Add the payout (with multiplier) to the total win for this spin.
		if (isThirdParty)	// CSN-5756 - Treasure Island. If third party game, increase with the WHOLE total win
			increaseTotalWin( nPayout * multiplier * getBetPerLine());
		else
			increaseTotalWin( nPayout * multiplier );
    
		// Add a scatter to the spin.
		SlotScatter* pScatter = spin->addScatter( nScatterPositions,
			nPayout * getBetPerLine(), multiplier, false, srScatterResult, 0 );

		// If the free spin multiplier was used...
		if ( multiplier == MULTIPLIER_FREE_SPIN )
		{
			// then add the free spin note.
			pScatter->addNote( NOTE_FREE_SPIN );
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::addButterflyBonusScatters()
///
/// This function determines if the current spin wins any Butterfly bonus
/// scatter payments and adds the scatters.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::addButterflyBonusScatters( SlotSpin* spin )
{
	// Loop through each reel.
  	for( int reel = 0; reel < getReelCount(); reel++ )
	{
		// The position bit for the current icon in case it needs to be stored in a mask.
		uint8 position = reel;

		// Loop through each row in the wheel house.
		for( int row = 0; row < 3; row++ )
		{
			// Get the current stop.
			uint8 stop = relativeWheelPosition( getGameModeId(), reel,
				spin->getStops()[ reel ], row );

			// Get the icon for the current stop.
			uint8 currentIcon = getIcon( getGameModeId(), reel, stop )->iconid;
			
			// If the icon is the one we are looking for...
			if ( currentIcon == ICON_BUTTERFLY )
			{
				// Get the base payout for the scatter.
				int32 nPayout = getRandomValueFromProbabilityTable( PROB_TABLE_BUTTERFLY_BONUS_PRIZE );
				
				// Scatters payout based on the total bet, so factor in the number of bet lines.
				nPayout *= getNumLinesBet();

				// Add the payout to the total win for this spin.
				if (isThirdParty)	// CSN-5756 - Treasure Island. If third party game, increase with the WHOLE total win
					increaseTotalWin( nPayout*getBetPerLine() );
				else
					increaseTotalWin( nPayout );
		    
				// Add a scatter to the spin.
				spin->addScatter( 1 << position, nPayout * getBetPerLine(), 1, false, EVT_BUTTERFLY_BONUS, 0 );
			}

			// Move to the next bit in the position mask.
            position += 5;
		}
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::evaluateFeatureTrigger()
///
/// This function determines if the current spin triggered the free game
/// feature or the bonus game and adds the appropriate scatter.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::evaluateFeatureTrigger( SlotSpin* spin, int32 nNumScatterIcons, int32 nScatterPositions )
{
	// If we have at least 1 scatter icon...
	if ( nNumScatterIcons >= 1 )
	{
		// then we will at the very least have a near miss.
		SpinResult srFeatureTrigger = EVT_FEATURE_NEAR_MISS;

		// We need a place to keep up with how many free spins to award.
		int32 nFreeSpins = 0;

		// If there were at least 3 scatter icons...
		if ( nNumScatterIcons >= 3 )
		{
			// then we triggered one of the features.  Let's see which one.
			bool bFreeGames = true;

			// If we are not already in the free game feature...
			if ( getGameModeId() != MODE_FREE_SPIN )
			{
				// then pick one at random.
				if ( getRandomValueFromProbabilityTable( PROB_TABLE_WHICH_FEATURE ) == FEATURE_PARADISE_PICK )
				{
					bFreeGames = false;
				}

				if(m_bDebugSpinsMode && g_nDebugForceTrigger >= 0)
				{
					if(g_nDebugForceTrigger == 0)
						bFreeGames = true;
					else
						bFreeGames = false;
				}
			}
			
			// If they win the free game feature...
			if ( bFreeGames )
			{
				setGameModeId( MODE_FREE_SPIN );

				// The spin result is not a near miss any more.
				srFeatureTrigger = EVT_FREE_GAME_FEATURE;

				// Award 10 free spins since it actually triggered.
				nFreeSpins = 10;

				// Add the free spins.
				incrementFreeSpins( nFreeSpins );

				if(m_bDebugSpinsMode)
				{
					//reset debug data
					g_nDebugForceTrigger = -1;
					g_nDebugButterFly = -1;
					g_nDebugPrizeIndex = 0;
					for(int x=0; x<10; x++)
						g_nDebugPrize[x] = -1;
				}
			}
			else
			{
				// The spin result is not a near miss any more.
				srFeatureTrigger = EVT_PARADISE_PICK_FEATURE;

				// The game can't complete until the bonus game is done.
				setGameCanComplete( false );

				// CSN-5756 - Treasure Island
				// If third party games, there are two bonus games types, both work exactly the same. But we have to send the client
				// one of the two types.
				if (isThirdParty)
				{
					int32 iBonusType = getRandomValueFromProbabilityTable( PROB_TABLE_WHICH_FEATURE );
					if (iBonusType == 0)
						setBonusGameState( BONUS_GAME_ACTIVE );
					else
						setBonusGameState( BONUS_GAME_ACTIVE_TYPE2 );
				}
				else
				{
					// If Paradise Dreams game, just a bonus game type
					setBonusGameState( BONUS_GAME_ACTIVE );
				}

				// Initialize the current picks remaining.
				setCurrentPicksRemaining( 5 );

				if(m_bDebugSpinsMode && g_nDebugButterFly >=0)
				{
					setAwardExtraPicks( false );

					//force the butterfly right away
					if(g_nDebugButterFly == 1)
					{
						addExtraPicks( 5 );
						setAwardExtraPicks( false );
					}
				}
				else
				{
					// See if they are going to get the extra picks at some point in the feature.
					if ( getRandomValueFromProbabilityTable( PROB_TABLE_AWARD_EXTRA_PICKS ) )
					{
						// We will randomly give them the exta picks now or maybe later.
						if ( getRandomLessThan( 6 ) == 0 )
						{
							// Go ahead and give them the extra picks now.
							addExtraPicks( 5 );
							setAwardExtraPicks( false );
						}
						// Otherwise...
						else
						{
							// Give them the extra picks later.
							setAwardExtraPicks( true );
						}
					}
					// Otherwise...
					else
					{
						// they do not get any extra picks this time.
						setAwardExtraPicks( false );
					}
				}

				// Initialize each Paradise Pick choice.
				for ( int n = 0; n < NUM_PARADISE_PICK_CHOICES; n++ )
				{
					setPrize( n, PARADISE_PICK_PRIZE_INVALID );
				}
			}
		}

		// Add a scatter to the spin for the near miss or the feature trigger.
		spin->addScatter( nScatterPositions, 0, 0, false,
			srFeatureTrigger, nFreeSpins );
	}
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::classifyScatters( SlotSpin* spin )
///
/// @param   spin       Pointer to the spin with stop data.
///
/// This is an override of the framework method.  
/// It determines if the current spin merits free spins or scatter payments.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::classifyScatters( SlotSpin* spin )
{	
	// Try for the Random Progressive Jackpot
	tryForJackpot( spin );

	// Declare some local variables to track the scatter positions.
	int32 nScatterPositions = 0;
	int32 nNumScatterIcons = 0;

	// See if there is a scatter win.
	SpinResult srResult = getScatterWinResult( spin, nNumScatterIcons, nScatterPositions );

	// Add the scatter, if any.
	evaluateScatterWin( spin, srResult, nScatterPositions );

	// Add the Butterfly bonus scatters if any.
	addButterflyBonusScatters( spin );

	// Add the scatters for the free game feature.
	evaluateFeatureTrigger( spin, nNumScatterIcons, nScatterPositions );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::isValidGuts()   
///
/// @return	 bool8      True if gut is valid.    
///
/// This helper method is used to see if the current gut ID is valid.
////////////////////////////////////////////////////////////////////////////
bool8 ParadiseDreamsGuts::isValidGuts()
{
	// Make sure that the m_aLineWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	// Note:  we can't do this in the constructor because we have to call
	// getReelCount.
	ASSERT( countof( m_aLineWinSpinResults[0] ) == getReelCount( MODE_NORMAL ) + 1 );

	// Make sure that the m_aScatterWinSpinResults array has an entry for each reel,
	// and an additional entry in case there are 0 icons on the line.
	ASSERT( countof( m_aScatterWinSpinResults ) == getReelCount( MODE_NORMAL ) + 1 );

	// Now make sure that both game modes have the same number of reels.
	ASSERT( getReelCount( MODE_NORMAL ) == getReelCount( MODE_FREE_SPIN ) );

	// Finally just check to see if the gut is within the right range.
    //return ( getGutsId() >= 3100 && getGutsId() < 3200 );		//CSN-5671
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier, uint8& nNoteId )
///
/// @param   nGameModeMultiplier	The multiplier for the game mode.
/// @param   nNoteId				The note ID for the game mode.
///
/// This helper method is used to get the game mode multiplier and note ID.
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::getGameModeMultiplierAndNoteId( uint8& nGameModeMultiplier,
	uint8& nNoteId )
{
	// If we are in free spin mode...
    if ( getGameModeId() == MODE_FREE_SPIN )
    {
		// then return the free spin multiplier...
        nGameModeMultiplier = MULTIPLIER_FREE_SPIN;

		// and the free spin note.
        nNoteId = NOTE_FREE_SPIN;
    }
	// Otherwise...
    else
    {
		// just defer to the base class.
        SlotGuts::getGameModeMultiplierAndNoteId( nGameModeMultiplier, nNoteId );
    }
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::hasSavedGames()
///
/// @param   bool8   True/False indicating if this machine supports saved gamees.
///
/// This is a framework override to determine if this machine supports saved games.
////////////////////////////////////////////////////////////////////////////
bool8 ParadiseDreamsGuts::hasSavedGames()
{
	// We support saved games so always return true.
	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::processClientData()
///
/// @param   bonusGameType      The current type of bonus game.
/// @param   bonusDataCount     The number of integers passed from the client.
/// @param   bonusData			An array of integers passed from the client.
/// 
/// This is an override of the framework method.
/// 
////////////////////////////////////////////////////////////////////////////
bool8 ParadiseDreamsGuts::processClientData( uint8 bonusGameType, int32 bonusDataCount, int32 *bonusData )
{
	// Make sure the bonus game type is correct (in case somebody is trying to hack us)
	if ( bonusGameType != BONUS_GAME_PARADISE_PICK )
	{
		return false;
	}

	// Make sure the data count is correct (in case somebody is trying to hack us)
	if ( bonusDataCount != 1 )
	{
		return false;
	}

	// Make sure the pointer is not NULL.  I'm not sure how this could happen, but we
	// don't want to crash if it does.
	if ( bonusData == NULL )
	{
		ASSERT( false );
		return false;
	}

	// Store the index that they picked in a local variable.
	int32 nPickIndex = *bonusData;

	// Make sure the pick index is valid (in case somebody is trying to hack us)
	if ( nPickIndex < 0 || nPickIndex >= NUM_PARADISE_PICK_CHOICES )
	{
		return false;
	}

	// Make sure we have picks remaining (in case somebody is trying to hack us)
	if ( getCurrentPicksRemaining() == 0 )
	{
		return false;
	}

	// Make sure they have not already picked this index before
	if ( getPrize( nPickIndex ) != PARADISE_PICK_PRIZE_INVALID )
	{
		return false;
	}

	// Initialize a local to hold the prize.
	int32 nPrize = getRandomValueFromProbabilityTable( PROB_TABLE_PICK_PRIZE );

	if(m_bDebugSpinsMode)
	{
		if(g_nDebugPrize[getCurrentPicksRemaining() - 1] > 0)
		{
			nPrize = g_nDebugPrize[getCurrentPicksRemaining() - 1];
		}
	}

	// Persist the local variable in the subgame data.
	setPrize( nPickIndex, nPrize );

	// We just finished a pick, so subtract 1 from the counter.
	decrementCurrentPicksRemaining();

	// If we are supposed to give them the extra picks at some point...
	if ( getAwardExtraPicks() )
	{
		// if they just ran out of picks...
		if ( getCurrentPicksRemaining() == 0 )
		{
			// then we must go ahead and give them the extra picks now since this is the last chance.
			addExtraPicks( 5 );
			setAwardExtraPicks( false );
		}
		// Otherwise we will randomly give them the exta picks now.
		else if ( getRandomLessThan( getCurrentPicksRemaining() + 1 ) == 0 )
		{
			// Go ahead and give them the extra picks now.
			addExtraPicks( 5 );
			setAwardExtraPicks( false );
		}
	}

	// If we are out of picks...
	if ( getCurrentPicksRemaining() == 0 )
	{
		// Calculate the bonus prize.
		int32 nBasePayoutAmount = getBetPerLine() * getNumLinesBet();
		int32 nTotalMultiplier = getTotalPrize();
		int32 nTotalBonusPayoutAmount = nBasePayoutAmount * nTotalMultiplier;

		// Add the bonus game result.
		if (isThirdParty)		// CSN-5756 - Treasure Island. If third party game, increase with the WHOLE total win
			addBonusGameThirdParty(BONUS_GAME_PARADISE_PICK, 0, nBasePayoutAmount, nTotalMultiplier, nTotalBonusPayoutAmount, 0); 
		else
			addBonusGame( BONUS_GAME_PARADISE_PICK, 0, nBasePayoutAmount, nTotalMultiplier, nTotalBonusPayoutAmount, 0 );

		// the bonus game is done.
		setGameCanComplete( true );
		setBonusGameState( BONUS_GAME_INACTIVE );

		if(m_bDebugSpinsMode)
		{
			//reset debug data
			g_nDebugForceTrigger = -1;
			g_nDebugButterFly = -1;
			g_nDebugPrizeIndex = 0;
			for(int x=0; x<10; x++)
				g_nDebugPrize[x] = -1;
		}
	}

	return true;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getCurrentPicksRemaining()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
int32 ParadiseDreamsGuts::getCurrentPicksRemaining()
{
	// Return the sub-game data.
	return m_subGame->getData( SGDIDX_CURRENT_PICKS_REMAINING );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::setCurrentPicksRemaining()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
void  ParadiseDreamsGuts::setCurrentPicksRemaining( int32 nCurrentPicksRemaining )
{
	// Set the value in the sub-game data.
    m_subGame->update( SGDIDX_CURRENT_PICKS_REMAINING, nCurrentPicksRemaining );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::addExtraPicks()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::addExtraPicks( int32 nExtraPicks )
{
	// Get the current value and add the extra picks.
    m_subGame->update( SGDIDX_CURRENT_PICKS_REMAINING, getCurrentPicksRemaining() + nExtraPicks );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::decrementCurrentPicksRemaining()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::decrementCurrentPicksRemaining()
{
	// Get the current value and subtract 1.
    m_subGame->update( SGDIDX_CURRENT_PICKS_REMAINING, getCurrentPicksRemaining() - 1 );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getAwardExtraPicks()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
bool ParadiseDreamsGuts::getAwardExtraPicks()
{
	// Return the sub-game data.
	if ( m_subGame->getData( SGDIDX_AWARD_EXTRA_PICKS ) )
	{
		return true;
	}

	return false;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::setAwardExtraPicks()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::setAwardExtraPicks( bool bAwardExtraPicks )
{
	// Set the value in the sub-game data.
    m_subGame->update( SGDIDX_AWARD_EXTRA_PICKS, bAwardExtraPicks );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getPrize()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
int32 ParadiseDreamsGuts::getPrize( int32 nPickIndex )
{
	ASSERT( nPickIndex >= 0 && nPickIndex < NUM_PARADISE_PICK_CHOICES );

	// Return the sub-game data.
	return m_subGame->getData( SGDIDX_PARADISE_PICK_PRIZE_0 + nPickIndex );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::setPrize()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::setPrize( int32 nPickIndex, int32 nPrize )
{
	ASSERT( nPickIndex >= 0 && nPickIndex < NUM_PARADISE_PICK_CHOICES );

	// Set the value in the sub-game data.
    m_subGame->update( SGDIDX_PARADISE_PICK_PRIZE_0 + nPickIndex, nPrize );
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getTotalPrize()
///
/// This is a sub-game data helper.
/// 
////////////////////////////////////////////////////////////////////////////
int32 ParadiseDreamsGuts::getTotalPrize()
{
	// Declare a local variable to track the total.
	int32 nRV = 0;

	// Loop through all of the possible choices.
	for ( int n = 0; n < NUM_PARADISE_PICK_CHOICES; n++ )
	{
		// Get the prize (if any) for the current item.
		int32 nPrize = getPrize( n );

		// If this item awarded a prize...
		if ( nPrize != PARADISE_PICK_PRIZE_INVALID )
		{
			// then add it to the total.
			nRV += nPrize;
		}
	}

	// Return the total.
	return nRV;
}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::Debug_SetLoserStops( uint8* pStops )
///
/// @param   pStops		The stops to set.
///
/// Base class override, returns a losing spin triggered by the debug tool
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::Debug_SetLoserStops( uint8* pStops )
{
	pStops[0]=6;
	pStops[1]=4;
	pStops[2]=0;
	pStops[3]=0;
	pStops[4]=0;
}

////////////////////////////////////////////////////////////////////////////
/// Debug spins functionality, can control whether or not the butterfly appears
/// and what prizes are won during the feature game
////////////////////////////////////////////////////////////////////////////
void ParadiseDreamsGuts::initDebugFunctionMap()
{
	SlotGuts::initDebugFunctionMap();

	addDebugStopFunction( EVT_DEBUG_FUNCTION_SPINS, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_FreeSpins) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_FEATURE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Feature) );


	addDebugStopFunction( EVT_DEBUG_FUNCTION_BUTTERFLY_YES, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_ButterFlyYes) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_BUTTERFLY_NO, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_ButterFlyNo) );


	addDebugStopFunction( EVT_DEBUG_FUNCTION_1X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_1X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_2X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_2X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_3X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_3X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_5X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_5X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_10X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_10X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_20X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_20X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_50X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_50X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_100X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_100X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_250X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_250X) );

	addDebugStopFunction( EVT_DEBUG_FUNCTION_1000X_PRIZE, 
		new DebugStopFunc<ParadiseDreamsGuts>(this, &ParadiseDreamsGuts::DebugFeature_Prize_1000X) );
}

int ParadiseDreamsGuts::g_nDebugForceTrigger = -1;
int ParadiseDreamsGuts::g_nDebugButterFly = -1;
int ParadiseDreamsGuts::g_nDebugPrizeIndex = 0;
int ParadiseDreamsGuts::g_nDebugPrize[10] = { -1 };

void ParadiseDreamsGuts::DebugFeature_FreeSpins(SlotSpin *spin )
{
	g_nDebugForceTrigger = 0;
}
void ParadiseDreamsGuts::DebugFeature_Feature(SlotSpin *spin )
{
	g_nDebugForceTrigger = 1;
}

void ParadiseDreamsGuts::DebugFeature_ButterFlyYes(SlotSpin *spin )
{
	g_nDebugButterFly = 1;
}
void ParadiseDreamsGuts::DebugFeature_ButterFlyNo(SlotSpin *spin )
{
	g_nDebugButterFly = 0;
}

void ParadiseDreamsGuts::DebugFeature_Prize_1X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 1;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_2X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 2;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_3X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 3;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_5X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 5;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_10X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 10;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_20X(SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 20;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_50X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 50;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_100X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 100;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_250X( SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 250;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}
void ParadiseDreamsGuts::DebugFeature_Prize_1000X(SlotSpin *spin )
{
	g_nDebugPrize[g_nDebugPrizeIndex] = 1000;

	g_nDebugPrizeIndex++;
	if(g_nDebugPrizeIndex >= 10)
		g_nDebugPrizeIndex = 0;
}

// CSN-5756 - Treasure Island. If third party game, increase with the WHOLE total win
BonusGame*
ParadiseDreamsGuts::addBonusGameThirdParty(uint8 bonusgametype,uint8 bonusgameindex,int32 basepayoutamount,int32 totalmultiplier,int32 totalbonuspayoutamount,int32 freespins)
{
	increaseTotalWin(totalbonuspayoutamount);
	
	SlotSpin* spin = getSlotSpin();
	return spin->addBonusGame(bonusgametype, bonusgameindex, basepayoutamount, totalmultiplier, totalbonuspayoutamount, 0, 0);

}

////////////////////////////////////////////////////////////////////////////
/// @fn 	 ParadiseDreamsGuts::getProgressiveValue( )
///
/// Returns current jackpot value for ParadiseDreamsGuts guts
////////////////////////////////////////////////////////////////////////////
int32 ParadiseDreamsGuts::getProgressiveValue()
{
   SlotsSQL *slotTracker = (SlotsSQL *) tracker;
   int32 jackpot = 0;

    if ( tracker )
    {
    	slotTracker->lock();
		slotTracker->getJackpot(getJackpotId(), jackpot, getInfo()->forMoney, 1);
    	slotTracker->unlock();
    }

	return jackpot;
}